/**
 *    Copyright 2009-2015 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package org.apache.ibatis.mapping;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.apache.ibatis.cache.Cache;
import org.apache.ibatis.executor.keygen.Jdbc3KeyGenerator;
import org.apache.ibatis.executor.keygen.KeyGenerator;
import org.apache.ibatis.executor.keygen.NoKeyGenerator;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.scripting.LanguageDriver;
import org.apache.ibatis.session.Configuration;

/**
 * @author Clinton Begin
 */
/**
 * 映射的语句----映射的一个select/update/create/insert节点<描述的一条语句>
 *
 */
public final class MappedStatement {
	/**
	 * 来源，一般为文件名或者是注解的类名
	 */
	private String resource;
	/**
	 * 配置
	 */
	private Configuration configuration;
	/**
	 * 标志符，可以用于缓存
	 */
	private String id;
	/**
	 * 每次查询的行数（可选）
	 */
	private Integer fetchSize;
	/**
	 * 超时时间
	 */
	private Integer timeout;
	/**
	 * 语句类型，决定最后将使用Statement, PreparedStatement还是CallableStatement进行查询
	 */
	private StatementType statementType;
	/**
	 * 结果集的读取类型，与java.sql.ResultSet中的类型对应。
	 */
	private ResultSetType resultSetType;
	/**
	 * Mybatis中的sqlSource，保存了初次解析的结果
	 */
	private SqlSource sqlSource;
	/**
	 * 缓存空间
	 */
	private Cache cache;
	/**
	 * 保存了方法参数与sql语句中的参数对应关系
	 */
	private ParameterMap parameterMap;
	/**
	 * 可选，定义结果集与Java类型中的字段映射关系
	 */
	private List<ResultMap> resultMaps;
	/**
	 * 是否刷新缓存
	 */
	private boolean flushCacheRequired;
	/**
	 * 是否使用缓存
	 */
	private boolean useCache;
	/**
	 * 可选，嵌套结果select语句，默认为false
	 */
	private boolean resultOrdered;
	/**
	 * Sql执行类型（增、删、改、查）
	 */
	private SqlCommandType sqlCommandType;
	/**
	 * 可选，键生成器
	 */
	private KeyGenerator keyGenerator;
	/**
	 * 可选，作为键的属性
	 */
	private String[] keyProperties;
	/**
	 * 可选，键的列
	 */
	private String[] keyColumns;
	/**
	 * 是否有嵌套的映射关系
	 */
	private boolean hasNestedResultMaps;
	/**
	 * 数据库的id
	 */
	private String databaseId;
	/**
	 * logger
	 */
	private Log statementLog;
	/**
	 * 解析器
	 */
	private LanguageDriver lang;
	/**
	 * 可选，数据集的名称
	 */
	private String[] resultSets;

	MappedStatement() {
		// constructor disabled
	}

	/**
	 * 静态内部类，建造者模式
	 */
	public static class Builder {
		private MappedStatement mappedStatement = new MappedStatement();

		public Builder(Configuration configuration, String id, SqlSource sqlSource, SqlCommandType sqlCommandType) {
			mappedStatement.configuration = configuration;
			mappedStatement.id = id;
			mappedStatement.sqlSource = sqlSource;
			mappedStatement.statementType = StatementType.PREPARED;
			mappedStatement.parameterMap = new ParameterMap.Builder(configuration, "defaultParameterMap", null,
					new ArrayList<ParameterMapping>()).build();
			mappedStatement.resultMaps = new ArrayList<ResultMap>();
			mappedStatement.timeout = configuration.getDefaultStatementTimeout();
			mappedStatement.sqlCommandType = sqlCommandType;
			mappedStatement.keyGenerator = configuration.isUseGeneratedKeys()
					&& SqlCommandType.INSERT.equals(sqlCommandType) ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
			String logId = id;
			// 指定MyBatis中的日志文件名称的后缀如果不为空
			if (configuration.getLogPrefix() != null) {
				// 设置日志id
				logId = configuration.getLogPrefix() + id;
			}
			mappedStatement.statementLog = LogFactory.getLog(logId);
			mappedStatement.lang = configuration.getDefaultScriptingLanuageInstance();
		}

		/**
		 * 设置来源，一般为文件名或者是注解的类名
		 * 
		 * @param resource
		 *            来源
		 * @return
		 */
		public Builder resource(String resource) {
			mappedStatement.resource = resource;
			return this;
		}

		/**
		 * 得到id
		 * 
		 * @return
		 */
		public String id() {
			return mappedStatement.id;
		}

		/**
		 * 设置参数，保存了方法参数与sql语句中的参数对应关系
		 * 
		 * @param parameterMap
		 * @return
		 */
		public Builder parameterMap(ParameterMap parameterMap) {
			mappedStatement.parameterMap = parameterMap;
			return this;
		}

		/**
		 * 设置结果集与Java类型中的字段映射关系
		 * 
		 * @param resultMaps
		 * @return
		 */
		public Builder resultMaps(List<ResultMap> resultMaps) {
			mappedStatement.resultMaps = resultMaps;
			for (ResultMap resultMap : resultMaps) {
				mappedStatement.hasNestedResultMaps = mappedStatement.hasNestedResultMaps
						|| resultMap.hasNestedResultMaps();
			}
			return this;
		}

		/**
		 * 设置每次需要查询的行数（可选）
		 * 
		 * @param fetchSize
		 * @return
		 */
		public Builder fetchSize(Integer fetchSize) {
			mappedStatement.fetchSize = fetchSize;
			return this;
		}

		/**
		 * 设置超出时间
		 * 
		 * @param timeout
		 * @return
		 */
		public Builder timeout(Integer timeout) {
			mappedStatement.timeout = timeout;
			return this;
		}

		/**
		 * 设置语句类型
		 * 
		 * @param statementType
		 * @return
		 */
		public Builder statementType(StatementType statementType) {
			mappedStatement.statementType = statementType;
			return this;
		}

		/**
		 * 设置结果集
		 * 
		 * @param resultSetType
		 * @return
		 */
		public Builder resultSetType(ResultSetType resultSetType) {
			mappedStatement.resultSetType = resultSetType;
			return this;
		}

		/**
		 * 设置缓存
		 * 
		 * @param cache
		 * @return
		 */
		public Builder cache(Cache cache) {
			mappedStatement.cache = cache;
			return this;
		}

		/**
		 * 设置是否刷新缓存
		 * 
		 * @param flushCacheRequired
		 * @return
		 */
		public Builder flushCacheRequired(boolean flushCacheRequired) {
			mappedStatement.flushCacheRequired = flushCacheRequired;
			return this;
		}

		/**
		 * 设置resultOrdered
		 * 
		 * @param useCache
		 * @return
		 */
		public Builder useCache(boolean useCache) {
			mappedStatement.useCache = useCache;
			return this;
		}

		/**
		 * 设置resultOrdered
		 * 
		 * @param resultOrdered
		 * @return
		 */
		public Builder resultOrdered(boolean resultOrdered) {
			mappedStatement.resultOrdered = resultOrdered;
			return this;
		}

		/**
		 * 设置键生成器(可选)
		 * 
		 * @param keyGenerator
		 * @return
		 */
		public Builder keyGenerator(KeyGenerator keyGenerator) {
			mappedStatement.keyGenerator = keyGenerator;
			return this;
		}

		/**
		 * 设置作为键的属性(可选)
		 * 
		 * @param keyProperty
		 * @return
		 */
		public Builder keyProperty(String keyProperty) {
			mappedStatement.keyProperties = delimitedStringtoArray(keyProperty);
			return this;
		}

		/**
		 * 设置键的列(可选)
		 * 
		 * @param keyColumn
		 * @return
		 */
		public Builder keyColumn(String keyColumn) {
			mappedStatement.keyColumns = delimitedStringtoArray(keyColumn);
			return this;
		}

		/**
		 * 设置数据库的id
		 * 
		 * @param databaseId
		 * @return
		 */
		public Builder databaseId(String databaseId) {
			mappedStatement.databaseId = databaseId;
			return this;
		}

		/**
		 * 设置解析器
		 * 
		 * @param driver
		 * @return
		 */
		public Builder lang(LanguageDriver driver) {
			mappedStatement.lang = driver;
			return this;
		}

		/**
		 * 设置数据集的名称(可选)
		 * 
		 * @param resultSet
		 * @return
		 */
		public Builder resulSets(String resultSet) {
			mappedStatement.resultSets = delimitedStringtoArray(resultSet);
			return this;
		}

		/**
		 * 创建
		 * 
		 * @return
		 */
		public MappedStatement build() {
			assert mappedStatement.configuration != null;
			assert mappedStatement.id != null;
			assert mappedStatement.sqlSource != null;
			assert mappedStatement.lang != null;
			mappedStatement.resultMaps = Collections.unmodifiableList(mappedStatement.resultMaps);
			return mappedStatement;
		}
	}

	/**
	 * 得到键生成器
	 * 
	 * @return
	 */
	public KeyGenerator getKeyGenerator() {
		return keyGenerator;
	}

	/**
	 * 得到Sql执行类型（增、删、改、查）
	 * 
	 * @return
	 */
	public SqlCommandType getSqlCommandType() {
		return sqlCommandType;
	}

	/**
	 * 得到来源，一般为文件名或者是注解的类名
	 * 
	 * @return
	 */
	public String getResource() {
		return resource;
	}

	/**
	 * 得到配置
	 * 
	 * @return
	 */
	public Configuration getConfiguration() {
		return configuration;
	}

	/**
	 * 得到id
	 * 
	 * @return
	 */
	public String getId() {
		return id;
	}

	/**
	 * 判断是否有嵌套的映射关系
	 * 
	 * @return
	 */
	public boolean hasNestedResultMaps() {
		return hasNestedResultMaps;
	}

	/**
	 * 得到读取的数目(条数)
	 * 
	 * @return
	 */
	public Integer getFetchSize() {
		return fetchSize;
	}

	/**
	 * 得到超时时间
	 * 
	 * @return
	 */
	public Integer getTimeout() {
		return timeout;
	}

	/**
	 * 得到语句类型
	 * 
	 * @return
	 */
	public StatementType getStatementType() {
		return statementType;
	}

	/**
	 * 得到结果集的读取类型
	 * 
	 * @return
	 */
	public ResultSetType getResultSetType() {
		return resultSetType;
	}

	/**
	 * 得到sqlSource
	 * 
	 * @return
	 */
	public SqlSource getSqlSource() {
		return sqlSource;
	}

	/**
	 * 得到保存了方法参数与sql语句中的参数对应关系
	 * 
	 * @return
	 */
	public ParameterMap getParameterMap() {
		return parameterMap;
	}

	/**
	 * 得到结果集与Java类型中的字段映射关系
	 * 
	 * @return
	 */
	public List<ResultMap> getResultMaps() {
		return resultMaps;
	}

	/**
	 * 得到缓存空间
	 * 
	 * @return
	 */
	public Cache getCache() {
		return cache;
	}

	/**
	 * 判断是否刷新缓存
	 * 
	 * @return
	 */
	public boolean isFlushCacheRequired() {
		return flushCacheRequired;
	}

	/**
	 * 判断是否使用缓存
	 * 
	 * @return
	 */
	public boolean isUseCache() {
		return useCache;
	}

	/**
	 * 判断嵌套结果select语句是否开启
	 * 
	 * @return
	 */
	public boolean isResultOrdered() {
		return resultOrdered;
	}

	/**
	 * 得到数据库的id
	 * 
	 * @return
	 */
	public String getDatabaseId() {
		return databaseId;
	}

	/**
	 * 得到作为键的属性
	 * 
	 * @return
	 */
	public String[] getKeyProperties() {
		return keyProperties;
	}

	/**
	 * 得到键的列
	 * 
	 * @return
	 */
	public String[] getKeyColumns() {
		return keyColumns;
	}

	/**
	 * 得到语句日志
	 * 
	 * @return
	 */
	public Log getStatementLog() {
		return statementLog;
	}

	/**
	 * 得到解析器
	 * 
	 * @return
	 */
	public LanguageDriver getLang() {
		return lang;
	}

	/**
	 * 得到数据集的名称
	 * 
	 * @return
	 */
	public String[] getResulSets() {
		return resultSets;
	}

	public BoundSql getBoundSql(Object parameterObject) {
		// 其实就是调用sqlSource.getBoundSql
		BoundSql boundSql = sqlSource.getBoundSql(parameterObject);
		// 剩下的可以暂时忽略
		List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
		if (parameterMappings == null || parameterMappings.isEmpty()) {
			boundSql = new BoundSql(configuration, boundSql.getSql(), parameterMap.getParameterMappings(),
					parameterObject);
		}

		// check for nested result maps in parameter mappings (issue #30)
		// 在参数映射中检查嵌套结果映射
		for (ParameterMapping pm : boundSql.getParameterMappings()) {
			String rmId = pm.getResultMapId();
			if (rmId != null) {
				ResultMap rm = configuration.getResultMap(rmId);
				if (rm != null) {
					hasNestedResultMaps |= rm.hasNestedResultMaps();
				}
			}
		}

		return boundSql;
	}
	/**
	 * 将以逗号分隔的字符串用逗号分隔转换成字符数组
	 * @param in
	 * @return
	 */
	private static String[] delimitedStringtoArray(String in) {
		if (in == null || in.trim().length() == 0) {
			return null;
		} else {
			return in.split(",");
		}
	}

}
